// Copyright (C) ZHENG Zhong <heavyzheng nospam-at gmail D0T com>
//
// $Id: child_node_list.hpp 317 2009-01-12 11:10:14Z heavyzheng $
//

#ifndef ZZHENG_XML_DOM_LIBXML2_CHILD_NODE_LIST_HPP_20080726__
#define ZZHENG_XML_DOM_LIBXML2_CHILD_NODE_LIST_HPP_20080726__

#include <zzheng/xml/config.hpp>
#include <zzheng/xml/dom/types.hpp>
#include <zzheng/xml/dom/basic_node_ptr.hpp>
#include <zzheng/xml/dom/basic_node_iterator.hpp>
#include <zzheng/xml/dom/node_proxy.hpp>

#include <zzheng/xml/dom/libxml2/child_node.hpp>
#include <zzheng/xml/dom/libxml2/libxml2_fwd.hpp>

#include <cassert>

namespace zzheng {
namespace xml {
namespace dom {
namespace libxml2_ {

    class element;
    class text;
    class comment;

    //! This class represents a child node list under an element. It may be viewed as a container
    //! of pointers to child_node. This class provides a similar API as std::list.
    class child_node_list {

        friend class element;

    public:

        ////////////////////////////////////////////////////////////////////////////////////////////
        // typedefs
        //

        typedef basic_node_ptr<child_node>            auto_type;

        typedef child_node&                           reference;
        typedef const child_node&                     const_reference;

        typedef basic_node_iterator<child_node>       iterator;
        typedef basic_node_iterator<const child_node> const_iterator;

    public:

        ////////////////////////////////////////////////////////////////////////////////////////////
        // capacity
        //

        //! Checks if this container is empty.
        //! \return true if this container is empty, false otherwise.
        bool empty() const;

        //! Returns the number of items in this list.
        //! \return the number of items in this list.
        size_type size() const;

        ////////////////////////////////////////////////////////////////////////////////////////////
        // iterators
        //

        //! Returns an iterator to the first child_node of the list.
        //! \return an iterator to the first child_node of the list.
        iterator begin();

        //! Returns an iterator just past the last child_node of the list.
        //! \return an iterator just past the last child_node of the list.
        iterator end();

        //! Const version of begin().
        const_iterator begin() const;

        //! Const version of end().
        const_iterator end() const;

        ////////////////////////////////////////////////////////////////////////////////////////////
        // element access
        //

        //! Returns a reference to the first child node of this list.
        //! \pre !empty()
        //! \return a reference to the first child node of this list.
        //! \throws bad_dom_operation  if the list is empty.
        child_node& front();

        //! Const version of front().
        const child_node& front() const;

        //! Returns a reference to the last child node of this list.
        //! \pre !empty()
        //! \return a reference to the last child node of this list.
        //! \throws bad_dom_operation  if the list is empty.
        child_node& back();

        //! Const version of back().
        const child_node& back() const;

        ////////////////////////////////////////////////////////////////////////////////////////////
        // modifiers/clear
        //

        //! Removes all the child_node objects from this list.
        //! \post empty() == true
        void clear();

        ////////////////////////////////////////////////////////////////////////////////////////////
        // modifiers/push_*
        //

        //! Pushes an element as a child node to the end of this list.
        //! \post size() is one more.
        //! \param child  the element to push as a child node.
        //! \return pointer to the inserted element.
        basic_node_ptr<element> push_back(const element_proxy& child);

        //! Pushes a text as a child node to the end of this list.
        //! \post size() is one more.
        //! \param child  the text to push as a child node.
        //! \return pointer to the inserted text.
        basic_node_ptr<text> push_back(const text_proxy& child);

        //! Pushes a comment as a child node to the end of this list.
        //! \post size() is one more.
        //! \param child  the comment to push as a child node.
        //! \return pointer to the inserted comment.
        basic_node_ptr<comment> push_back(const comment_proxy& child);

        //! Pushes a child node to the end of this list. This function makes a recursive clone of
        //! the child node and pushes the cloned one.
        //! \param child  the child node to clone and push.
        void push_back(const child_node& child);

        //! Pushes a child node to the end of this list. If adopt_node, this function takes the
        //! ownership of the child node from the source DOM tree; if clone_node, this function
        //! clones the child node and pushes the cloned one, leaving the original child node
        //! unchanged.
        //! \param child  the child node to push.
        //! \param aoc    adopt_node or clone_node, default to clone_node.
        void push_back(child_node& child, adopt_or_clone_t aoc = clone_node);

        //! Pushes an element as a child node to the beginning of this list.
        //! \post size() is one more.
        //! \param child  the element to push as a child node.
        //! \return pointer to the inserted element.
        basic_node_ptr<element> push_front(const element_proxy& child);

        //! Pushes a text as a child node to the beginning of this list.
        //! \post size() is one more.
        //! \param child  the text to push as a child node.
        //! \return pointer to the inserted text.
        basic_node_ptr<text> push_front(const text_proxy& child);

        //! Pushes a comment as a child node to the beginning of this list.
        //! \post size() is one more.
        //! \param child  the comment to push as a child node.
        //! \return pointer to the inserted comment.
        basic_node_ptr<comment> push_front(const comment_proxy& child);

        //! Pushes a child node to the front of this list. This function makes a recursive clone of
        //! the child node and pushes the cloned one.
        //! \param child  the child node to clone and push.
        void push_front(const child_node& child);

        //! Pushes a child node to the front of this list. If adopt_node, this function takes the
        //! ownership of the child node from the source DOM tree; if clone_node, this function
        //! clones the child node and pushes the cloned one, leaving the original child node
        //! unchanged.
        //! \param child  the child node to push.
        //! \param aoc    adopt_node or clone_node, default to clone_node.
        void push_front(child_node& child, adopt_or_clone_t aoc = clone_node);

        ////////////////////////////////////////////////////////////////////////////////////////////
        // modifiers/pop_*
        //

        //! Pops the last child node from the list. The child node popped out is destroyed.
        //! \pre  !empty()
        //! \post size() is one less.
        //! \throws bad_dom_operation  if the list is empty.
        void pop_back();

        //! Pops the first child node from the list. The child node popped out is destroyed.
        //! \pre  !empty()
        //! \post size() is one less.
        //! \throws bad_dom_operation  if the list is empty.
        void pop_front();

        ////////////////////////////////////////////////////////////////////////////////////////////
        // modifiers/insert
        //

        //! Inserts an element to the list as a child node before the specified position.
        //! \post size() is one more.
        //! \param pos    the position before which the element is to be inserted.
        //! \param child  the element to insert as a child node.
        //! \return pointer to the inserted element.
        //! \throws bad_dom_operation  if $pos does not belong to this list.
        basic_node_ptr<element> insert(iterator pos, const element_proxy& child);

        //! Inserts a text to the list as a child node before the specified position.
        //! \post size() is one more.
        //! \param pos    the position before which the text is to be inserted.
        //! \param child  the text to insert as a child node.
        //! \return pointer to the inserted text.
        //! \throws bad_dom_operation  if $pos does not belong to this list.
        basic_node_ptr<text> insert(iterator pos, const text_proxy& child);

        //! Inserts a comment to the list as a child node before the specified position.
        //! \post size() is one more.
        //! \param pos    the position before which the comment is to be inserted.
        //! \param child  the comment to insert as a child node.
        //! \return pointer to the inserted comment.
        //! \throws bad_dom_operation  if $pos does not belong to this list.
        basic_node_ptr<comment> insert(iterator pos, const comment_proxy& child);

        //! Inserts a child node before a position of this list. This function makes a recursive
        //! clone of the child node and inserts the cloned one.
        //! \param pos    the position before which the child_node object is to be inserted.
        //! \param child  the child node to clone and insert.
        //! \return an iterator to the inserted child_node object.
        iterator insert(iterator pos, const child_node& child);

        //! Inserts a child node before a position of this list. If adopt_node, this function takes
        //! the ownership of the child node from the source DOM tree; if clone_node, this function
        //! clones the child node and inserts the cloned one, leaving the original child node
        //! unchanged.
        //! \param pos    the position before which the child_node object is to be inserted.
        //! \param child  the child node to insert.
        //! \param aoc    adopt_node or clone_node, default to clone_node.
        iterator insert(iterator pos, child_node& child, adopt_or_clone_t aoc = clone_node);


        ////////////////////////////////////////////////////////////////////////////////////////////
        // modifiers/erase
        //

        //! Removes and destroys the child node at the given position from the list.
        //! \post size() is one less.
        //! \param pos  the position of the child node to erase.
        //! \return an iterator to the child node after the erased one.
        iterator erase(iterator pos);

        //! Removes and destroys all the child nodes between the first position (included) and the
        //! last position (excluded). This function has undefined behavior if the first iterator
        //! cannot reach the last iterator by increasing itself.
        //! \post size() is N less (N is the number of child nodes erased).
        //! \param first  the first position (included) of the child node to erase.
        //! \param last   the last position (excluded) of the child node to erase.
        //! \return an iterator to the child node after the last erased one (thus, ==last).
        iterator erase(iterator first, iterator last);

    private:

        ////////////////////////////////////////////////////////////////////////////////////////////
        // private
        //

        //! Private constructor.
        //! \param px  pointer to libxml2 node whose child nodes are to be managed by this list.
        explicit child_node_list(xmlNode* px);

        //! Non-implemented copy constructor.
        child_node_list(const child_node_list&);

        //! Non-implemented copy assignment.
        child_node_list& operator=(const child_node_list&);

        //! Private destructor.
        ~child_node_list();

        //! Returns a pointer to the first child_node object, or null if this list is empty.
        const child_node* first_() const;

        //! Checks if the given iterator belongs to this list.
        //! \param i  the iterator to check.
        //! \throws bad_dom_operation  if the iterator does not belong to this list.
        void check_ownership_(iterator i);

        //! Inserts a libxml2 node as a child to the list, before the specified position.
        //! \post size() is one more.
        //! \param pos    the position before which the libxml2 node is to be inserted.
        //! \param child  the libxml2 node to insert.
        //! \return pointer to the inserted libxml2 node.
        //! \throws bad_dom_operation  if $pos does not belong to this list.
        xmlNode* insert_(iterator pos, xmlNode* child);

        //! Creates a libxml2 node without namespace from the element proxy object. Note: after
        //! the libxml2 node is created, this function sets its namespace to null. This is because
        //! namespace URI should be looked up under the context of the element. This function
        //! creates a stand-alone element but does not put the element into the DOM tree, so it is
        //! up to the caller function to set the element's namespace URI properly, after this
        //! element has been put into the DOM tree.
        xmlNode* create_node_(const element_proxy& child);

        //! Creates a libxml2 node from the text proxy object.
        xmlNode* create_node_(const text_proxy& child);

        //! Creates a libxml2 node from the comment proxy object.
        xmlNode* create_node_(const comment_proxy& child);

        xmlNode* raw() {
            assert(raw_ != 0 && "Underlying libxml2 node should not be null.");
            return raw_;
        }

        const xmlNode* raw() const {
            assert(raw_ != 0 && "Underlying libxml2 node should not be null.");
            return raw_;
        }

        element& owner();

        const element& owner() const;

    private:

        xmlNode* raw_; //!< The libxml2 element holding the child node list.

    }; // class child_node_list

} // namespace zzheng::xml::dom::libxml2_
} // namespace zzheng::xml::dom
} // namespace zzheng::xml
} // namespace zzheng

#endif // ZZHENG_XML_DOM_LIBXML2_CHILD_NODE_LIST_HPP_20080726__





